using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Threading;
using System.Xml;

namespace sforceExplorer
{
	public class InternalProxy
	{
		#region Event delegates and public delcarations
		public delegate void NewRequestEvent(SoapRecord soapRecord);
		public delegate void NewResponseEvent(SoapRecord soapRecord);

		public static event NewRequestEvent NewRequest;
		public static event NewResponseEvent NewResponse;
		#endregion

		#region Private variable declarations
		private static Socket clientSocket;
		private static byte[] read = new byte[1023];
		private static byte[] Buffer = null;
		private static Encoding ASCII = Encoding.ASCII;
		private static string CRLF = System.Environment.NewLine;
		private static byte[] RecvBytes  = new byte[4095];
		private static Int32 port = 8081;
		private static TcpListener tcplistener;
		private static Thread mainThread;
		private static bool stopProxy = false;
		private static bool isRunning = false;
		#endregion

		#region public properties
		public static bool IsRunning 
		{
			get { return isRunning; }
		}
		public static int ProxyPort 
		{
			get { return port; }
		}
		
		public static string ProxyServer 
		{
			get { return "localhost"; }
		}
		#endregion

		#region public/private constructors
		public InternalProxy()
		{
			port = GlobalProperties.InternalProxyPort;
			IPEndPoint	ep = new IPEndPoint(System.Net.Dns.GetHostByName("localhost").AddressList[0], GlobalProperties.InternalProxyPort);
			tcplistener = new TcpListener(ep);
			tcplistener.Start();
			System.Diagnostics.Trace.WriteLine("Listening on port " + port.ToString());

			mainThread = new Thread(new ThreadStart(init));
			mainThread.Start();
		}
		private InternalProxy(Socket socket) 
		{
			clientSocket = socket;
		}

		#endregion

		#region Thread start/stop
		public static void Stop() 
		{
			if (isRunning) 
			{
				stopProxy = true;
				tcplistener.Stop();
				mainThread.Abort(0);
				isRunning = false;
			}
		}
		private static void init() 
		{
			stopProxy = false;
			while (!stopProxy) 
			{
				isRunning = true;
				if (tcplistener.Pending()) 
				{
					Socket socket = tcplistener.AcceptSocket();
					InternalProxy webproxy = new InternalProxy(socket);
					Thread thread = new Thread(new ThreadStart(InternalProxy.run));
					thread.Start();
				}
			}
		}
		private static void run() 
		{
			string clientmessage = "";
			string sURL = " ";
			int bytes = readmessage(read, ref clientSocket, ref clientmessage);
			System.Diagnostics.Trace.WriteLine(clientmessage);
			if (bytes == 0) 
			{
				return;
			}

			int index1 = clientmessage.IndexOf(" ".ToCharArray()[0]);
			int index2 = clientmessage.IndexOf(" ".ToCharArray()[0], index1 + 1);
			if (index1 == -1 || index2 == -1)
			{
				throw new IOException();
			}

			string part1 = clientmessage.Substring(index1 + 1, index2 - index1);
			int index3 = part1.IndexOf(@"/".ToCharArray()[0], index1 + 8);
			int index4 = part1.IndexOf(" ".ToCharArray()[0], index1 + 8);
			int index5 = index4 - index3;
			sURL = part1.Substring(index1 + 3, part1.Length - index5 - 8);
			Socket IPsocket;

			try 
			{
				IPHostEntry IPHost = Dns.Resolve(sURL);

				string[] aliases = IPHost.Aliases;
				IPAddress[] address = IPHost.AddressList;

				IPEndPoint sEndpoint = new IPEndPoint(address[0], 80);
				IPsocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
				IPsocket.Connect(sEndpoint);

				string GET = clientmessage;
				byte[] ByteGet = ASCII.GetBytes(GET);

				SoapRecord soapRecord = new SoapRecord();
				if (NewRequest != null) 
				{
					soapRecord.Request = GetHTTPHeaders(clientmessage) + System.Environment.NewLine + GetSoapPart(clientmessage);
					NewRequest(soapRecord);
				}

				IPsocket.Send(ByteGet, ByteGet.Length, 0);

				string strRetPage = null;
				Int32 rBytes = IPsocket.Receive(RecvBytes, RecvBytes.Length, 0);
				strRetPage = strRetPage + ASCII.GetString(RecvBytes, 0, rBytes);
				System.Threading.Thread.Sleep(100);
				bool bStop = false;
				while (!bStop) 
				{
					rBytes = IPsocket.Receive(RecvBytes, RecvBytes.Length, System.Net.Sockets.SocketFlags.None);
					strRetPage = strRetPage + ASCII.GetString(RecvBytes, 0, rBytes);
					System.Diagnostics.Trace.WriteLine(strRetPage.Substring(strRetPage.Length - 25));
					if (strRetPage.ToLower().IndexOf(@"</soap:envelope>") > 0 || strRetPage.ToLower().IndexOf(@"</envelope>") > 0 || strRetPage.ToLower().IndexOf(@"</soapenv:envelope>") > 0)
						bStop = true;
				}
				System.Diagnostics.Trace.WriteLine("Out of read loop, blocking?? " + IPsocket.Blocking.ToString());
				if (strRetPage.IndexOf("HTTP/1.1 200 OK") > 0 ) 
				{
					strRetPage = strRetPage.Substring(strRetPage.IndexOf("HTTP/1.1 200 OK"));
				}

				IPsocket.Shutdown(SocketShutdown.Both);
				sendmessage(clientSocket, strRetPage);
				clientSocket.Close();
				if (NewResponse != null) 
				{
					soapRecord.Response = GetHTTPHeaders(strRetPage) + System.Environment.NewLine + GetSoapPart(strRetPage);
					NewResponse(soapRecord);
				}
			} 
			catch (Exception exc2) 
			{
				System.Diagnostics.Trace.WriteLine(exc2.ToString());
			}
		}
	
		#endregion

		#region Read and Send
		private static int readmessage(byte[] ByteArray, ref Socket s, ref string clientmessage) 
		{
			int Totalbytes = 0;
			bool bStop = false;
			StringBuilder sb = new StringBuilder();
			while (!bStop) 
			{
				byte[] ByteArray1 = new byte[2046];
				
				int bytes = s.Receive(ByteArray1, ByteArray1.Length, SocketFlags.None);
				Totalbytes += bytes;
				sb.Append(System.Text.Encoding.ASCII.GetString(ByteArray1).ToCharArray(), 0, bytes); 
				System.Diagnostics.Trace.WriteLine(sb.ToString().Substring(sb.ToString().Length - 2));
				if (sb.ToString().ToLower().IndexOf(@"</soap:envelope>") > 0 || sb.ToString().ToLower().IndexOf(@"</envelope>") > 0)
					bStop = true;
			}
			clientmessage = sb.ToString();
			return Totalbytes;
		}
		private static void sendmessage(Socket s, string message) 
		{
			Buffer = new byte[message.Length + 1];
			int length = ASCII.GetBytes(message, 0, message.Length, Buffer, 0);
			s.Send(Buffer, length, 0);
		}
		#endregion

		#region Message Parsing / Formatting
		private static string FormatSOAPMessage(XmlDocument soapMsg)
		{
			XmlTextWriter writer = null;
			StringWriter strWriter = new StringWriter();
			writer = new XmlTextWriter(strWriter);

			//Use indentation for readability.
			writer.Formatting = System.Xml.Formatting.Indented;
			writer.Indentation = 4;

			soapMsg.WriteTo(writer);
			writer.Flush();
			return strWriter.ToString();
		}
		private static string GetHTTPHeaders(string clientMessage) 
		{
			string part1;
			string part3 = "";
			try 
			{
				if (clientMessage.IndexOf("VsDebugger") > 0) 
				{
					part1 = clientMessage.Substring(0, clientMessage.IndexOf("VsDebugger"));
					string part2 = clientMessage.Substring(clientMessage.IndexOf("VsDebugger"));
					part3 = part2.Substring(part2.IndexOf(System.Environment.NewLine));
					part3 = part3.Substring(0, part3.IndexOf(@"<?xml"));
				} 
				else 
				{
					part1 = clientMessage.Substring(0, clientMessage.IndexOf(@"<?xml"));
				}
				return part1 + part3;
			} 
			catch 
			{
				return "header parse error.";
			}
		}
		private static string GetSoapPart(string clientMessage) 
		{
			if (clientMessage.IndexOf("<?xml") == -1)
				return "";
			else 
			{
				string soap = clientMessage.Substring(clientMessage.IndexOf(@"<?xml"));
				XmlDocument xDoc = new XmlDocument();
				try 
				{
					xDoc.LoadXml(soap);
					return FormatSOAPMessage(xDoc);
				} 
				catch 
				{
					return soap;
				}
			}
		}

		#endregion
	
	
	}
}
